# -*- coding: utf-8 -*-

import os
import math
import logging
from datetime import datetime
from pyproj import Proj

from ezdxf.math import Vec3
from geodaisy import converters
from sqlalchemy.orm.state import InstanceState


# 打印错误信息
def error_info(err):
    logging.error("Connect Pool Error: %s" % err)
    print("Connect Pool Error: ", err)


# 遍历对象,获取所有属性和值
# 如果是sqlalchemy的InstanceState类型不加入
def items(obj):
    cont = dict()
    for attr, value in obj.__dict__.items():
        if isinstance(value, InstanceState):
            continue
        cont[attr] = value
    return cont


# 将vec3类型转换维数组坐标
def vec3arr(vec3):
    return [vec3.x, vec3.y, vec3.z]


# 将wkt二维坐标变换为三维vec
def xy2vec(x, y):
    return x, y, 0


# 将wkt数组转为vec数组
def wkt2vecs(wkt):
    return [xy2vec(c[0], c[1]) for c in wkt['coordinates']]


# 将wkt二维坐标变换为'xyseb'
def xy2xyseb(x, y):
    return x, y, 0, 0, 0


# 将wkt数组转为'xyseb'数组
def wkt2xysebs(wkt):
    return [xy2xyseb(c[0], c[1]) for c in wkt['coordinates']]


# 将wkt二维坐标变换为'insert
def wkt2insert(wkt):
    return [wkt['coordinates'][0], wkt['coordinates'][1], 0]


# AutoCAD 在内部使用任意轴算法为使用对象坐标的所有实体实现对象坐标系的任意但一致的生成。
# 给定要用作坐标系z轴的单位长度向量, 任意轴算法将为坐标系生成相应的x轴y轴遵循右手规则。
# 我们正在寻找任意x轴和y轴与正常的Az(任意z轴)一起使用。它们将被称为Ax和Ay(使用Vec3)。
# WCS 到 OCS
def wcs_to_ocs(entity, point):
    # normal (extrusion) vector
    Az = Vec3(entity.dxf.extrusion).normalize()
    if (abs(Az.x) < 1/64.) and (abs(Az.y) < 1/64.):
        # the cross-product operator
        Ax = Vec3(0, 1, 0).cross(Az).normalize()
    else:
        # the cross-product operator
        Ax = Vec3(0, 0, 1).cross(Az).normalize()
    Ay = Az.cross(Ax).normalize()

    # point in WCS
    px, py, pz = Vec3(point)
    x = px * Ax.x + py * Ax.y + pz * Ax.z
    y = px * Ay.x + py * Ay.y + pz * Ay.z
    z = px * Az.x + py * Az.y + pz * Az.z
    return Vec3(x, y, z)


# OCS 到 WCS
def ocs_to_wcs(entity, point):
    Wx = wcs_to_ocs(entity, (1, 0, 0))
    Wy = wcs_to_ocs(entity, (0, 1, 0))
    Wz = wcs_to_ocs(entity, (0, 0, 1))

    # point in OCS
    px, py, pz = Vec3(point)
    x = px * Wx.x + py * Wx.y + pz * Wx.z
    y = px * Wy.x + py * Wy.y + pz * Wy.z
    z = px * Wz.x + py * Wz.y + pz * Wz.z
    return Vec3(x, y, z)


# 获取当前时间
def dt():
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


# 格式化时间
def dt2str(t):
    return t.strftime('%Y-%m-%d %H:%M:%S') if t else None


# 字符串转时间
def str2dt(str):
    return datetime.strptime(str, '%Y-%m-%d %H:%M:%S')


# # wkb格式转字典
# def wkb2dict(geometry):
#     return mapping(wkb.loads(str(geometry), True))


# geometry格式转wkt
def geometry2wkt(geometry):
    return converters.geojson_to_wkt(geometry)


# 计算合理坐标
def normal(x, y):
    return x, y


# 计算整个地图的外边界
def box(l, r, t, b, p):
    l = p[0] if l is None else l
    r = p[0] if r is None else r
    t = p[1] if t is None else t
    b = p[1] if b is None else b
    l = p[0] if p[0] < l else l
    r = p[0] if p[0] > r else r
    b = p[1] if p[1] < b else b
    t = p[1] if p[1] > t else t
    return l, r, t, b


# 计算点是否在box内: True-在box内,False-不在box内
def point_in_box(p, l, r, t, b):
    return l <= p[0] <= r and b <= p[1] <= t


# 判断点是否在折线上
def point_at_polyline(point, polyline):
    for i in range(0, len(polyline) - 1):
        start_line, end_line, foot = polyline[i], polyline[i + 1], [0, 0]
        if foot_point(point, start_line, end_line, foot):
            if distance(point[0], point[1], foot[0], foot[1]) < THRESHOLD:
                return True
    return False


# 通过oda工具将dwg转换为dxf
def oda_dwg2dxf(dwg_path, dxf_path):
    cmd = r'ODAFileConverter.exe %s %s "ACAD2018" "DXF" "0" "0"' % (dwg_path, dxf_path)
    os.system(cmd)


# 通过oda工具将dxf转换dwg
def oda_dxf2dwg(dxf_path, dwg_path):
    cmd = r'ODAFileConverter.exe %s %s "ACAD2018" "DWG" "0" "0"' % (dxf_path, dwg_path)
    os.system(cmd)


# 将vec3转为数组
def insert2arr(insert):
    return [insert.x, insert.y, insert.z]


# cad中形成arc曲线的方法
def arc(x, y, r, sa=0, ea=360):
    start_angle, end_angle = sa, ea
    if sa > ea:
        start_angle = sa - 360

    result, i = [], start_angle
    while i < end_angle:
        _x = x + r * math.cos(i * math.pi / 180)
        _y = y + r * math.sin(i * math.pi / 180)
        result.append([_x, _y])
        i = i + 1
    return result


# cad中形成ellipse曲线的方法
def ellipse(cx, cy, ux, uy, vx, vy, sp=0.0, ep=math.pi*2):
    result, i = [], sp
    while i < ep + 0.1:
        _x = cx + math.cos(i) * ux + math.sin(i) * vx
        _y = cy + math.cos(i) * uy + math.sin(i) * vy
        result.append([_x, _y])
        i = i + 0.1
    return result


def csv_int(value):
    return int(value) if value else None


def csv_float(value):
    return float(value) if value else None


THRESHOLD = 0.000001


# 计算两点距离
def distance(x1, y1, x2, y2):
    return math.sqrt((x2-x1)**2 + (y2-y1)**2)


def is_point_same(p1, p2):
    return distance(p1[0], p1[1], p2[0], p2[1]) < THRESHOLD


def points_distance(points):
    _distance = 0
    for i in range(len(points) - 1):
        sp, ep = points[i], points[i + 1]
        _distance += distance(sp[0], sp[1], ep[0], ep[1])
    return _distance


# 计算点到线段的垂足: True-在线段上,False-不在线段上
def foot_point(p, ls, le, fp=None):
    d = le[0] - ls[0]
    if d == 0:
        fp[0] = ls[0]
        fp[1] = p[1]
    else:
        k = (le[1] - ls[1]) / d
        fp[0] = (k**2 * ls[0] + k * (p[1] - ls[1]) + p[0]) / (k**2 + 1)
        fp[1] = k * (fp[0] - ls[0]) + ls[1]

    max_x = ls[0] if ls[0] > le[0] else le[0]
    min_x = ls[0] if ls[0] < le[0] else le[0]
    max_y = ls[1] if ls[1] > le[1] else le[1]
    min_y = ls[1] if ls[1] < le[1] else le[1]
    return min_x <= fp[0] <= max_x and min_y <= fp[1] <= max_y


def k(x1, y1, x2, y2):
    return math.atan((y2 - y1) / (x2 - x1))


def twist(x, y, angle):
    a = -math.pi * angle / 180
    _x = x * math.cos(a) - y * math.sin(a)
    _y = x * math.sin(a) + y * math.cos(a)
    return _x, _y


def lonlat_to_2000(long, lat):
    p = Proj('+proj=tmerc +lat_0=0 +lon_0=114 +k=1 +x_0=38500000 +y_0=0 +ellps=GRS80 +units=m +no_defs')
    x, y = p(long, lat)
    return x - 38000000, y


def clear_same_points(track):
    for t in track:
        points = [t["points"][0]]
        for i in range(1, len(t["points"])):
            last = len(points) - 1
            x1, y1 = points[last][0], points[last][1]
            x2, y2 = t["points"][i][0], t["points"][i][1]
            if distance(x1, y1, x2, y2) < THRESHOLD:
                continue
            points.append([x2, y2])
        t["points"] = points


def lxy(lspt, lept, len):
    x, y = 0, 0
    if math.fabs(lspt[0] - lept[0]) < THRESHOLD:
        # 斜率不存在的情况
        x, y = lspt[0], lspt[1] + len
        if lept[1] < lspt[1]:
            y = lspt[1] - len
    else:
        dk = k(lspt[0], lspt[1], lept[0], lept[1])

        # 计算出可能在折线段上的两个坐标
        x1 = lspt[0] + len * math.cos(dk)
        y1 = lspt[1] + len * math.sin(dk)
        x2 = lspt[0] + len * math.cos(dk + math.pi)
        y2 = lspt[1] + len * math.sin(dk + math.pi)

        # 满足在折线段范围内的坐标点为正确的坐标
        if max(lspt[0], lept[0]) >= x1 >= min(lspt[0], lept[0]):
            x, y = x1, y1
        if max(lspt[0], lept[0]) >= x2 > min(lspt[0], lept[0]):
            x, y = x2, y2
    return [x, y]


def get_item_xy(tracks, postion, direction, d100):
    track, xygrid = tracks[postion['track_id']], {'x': None, 'y': None, 'grid': 0}
    if len(track['points']) < 2:
        xygrid['x'], xygrid['y'] = postion['x'], postion['y']
        return xygrid

    for i in range(len(track['points'])):
        point = track['points'][i]
        if distance(postion['x'], postion['y'], point[0], point[1]) < THRESHOLD:
            tlen = 0
            if direction == 0:
                if i == len(track['points']) - 1:
                    sp, ep = track['points'][i - 1], track['points'][i]
                    xygrid['grid'] = math.atan2(ep[1] - sp[1], ep[0] - sp[0])
                    xygrid['x'], xygrid['y'] = track['points'][i][0], track['points'][i][1]
                else:
                    for j in range(i, len(track['points']) - 1):
                        sp, ep = track['points'][j], track['points'][j + 1]
                        llen = distance(sp[0], sp[1], ep[0], ep[1])
                        if tlen + llen > d100:
                            slen = d100 - tlen
                            result = lxy(sp, ep, slen)
                            xygrid['grid'] = math.atan2(ep[1] - sp[1], ep[0] - sp[0])
                            xygrid['x'], xygrid['y'] = result[0], result[1]
                            tlen += llen
                            break
                        tlen += llen
                    if tlen < d100:
                        ii = len(track['points']) - 1
                        sp, ep = track['points'][ii - 1], track['points'][ii]
                        xygrid['grid'] = math.atan2(ep[1] - sp[1], ep[0] - sp[0])
                        xygrid['x'], xygrid['y'] = track['points'][-1][0], track['points'][-1][1]
            else:
                if i == 0:
                    sp, ep = track['points'][1], track['points'][0]
                    xygrid['grid'] = math.atan2(ep[1] - sp[1], ep[0] - sp[0])
                    xygrid['x'], xygrid['y'] = track['points'][0][0], track['points'][0][1]
                else:
                    for j in range(i, 0, -1):
                        sp, ep = track['points'][j], track['points'][j - 1]
                        llen = distance(sp[0], sp[1], ep[0], ep[1])
                        if tlen + llen > d100:
                            slen = d100 - tlen
                            result = lxy(sp, ep, slen)
                            xygrid['x'], xygrid['y'] = result[0], result[1]
                            xygrid['grid'] = math.atan2(ep[1] - sp[1], ep[0] - sp[0])
                            tlen += llen
                            break
                        tlen += llen
                    if tlen < d100:
                        sp, ep = track['points'][1], track['points'][0]
                        xygrid['grid'] = math.atan2(ep[1] - sp[1], ep[0] - sp[0])
                        xygrid['x'], xygrid['y'] = track['points'][0][0], track['points'][0][1]
    return xygrid


if __name__ == '__main__':
    x, y = lonlat_to_2000(113.260217, 44.028615)
    print(x, y)